package com.multireactor;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

public class MultiEchoEchoHandler implements Runnable{
    private Selector selector;
    private SocketChannel socketChannel;

    private ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
    private ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024);
    private int read=0;
    private int startIndex=0;

    public MultiEchoEchoHandler( Selector selector,SocketChannel socketChannel) throws Exception {
        this.selector = selector;
        this.socketChannel = socketChannel;
        SelectionKey key = socketChannel.register(selector, 0);
        key.attach(this);
        key.interestOps(SelectionKey.OP_READ);
        this.selector.wakeup();
    }
    /*读写出**/
    @Override
    public void run() {

        try{
            doRead();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void doRead() throws Exception{
        if(!socketChannel.isOpen()){
            return;
        }
        //存放的最大空间
        int canReadLen=buffer.remaining();
        //从流中读取的数据长度
        int readLen= socketChannel.read(readBuffer);
        //读取到数据
        if(readLen>0 && canReadLen>0){
            //流中数据，写模式变成读模式
            readBuffer.flip();
            //缓存中数据比流中读取数据大直接全读
            if(canReadLen>=readLen){
                //这个方法不改变position limit 位置
                buffer.put(buffer.position(),readBuffer,0,readLen);
                buffer.position(buffer.position()+readLen);
                //buffer还是写模式
                readBuffer.clear();
            }else {
                //存在一次读取不完的请，两次一定能读取完毕，(说明buffer 慢，进行)
                //一次读取不玩，做两次
                buffer.put(buffer.position(),readBuffer,0,canReadLen);
                buffer.position(buffer.position()+canReadLen);
                readBuffer.position(readBuffer.position()+canReadLen);
                //buffer依旧是写模式
                //处理一次数据
                readafterDeal();
                //变成写模式
                buffer.clear();
                int tmp=readBuffer.remaining();
                buffer.put(0,readBuffer,readBuffer.position(),tmp);
                buffer.position(tmp);
            }
            //找到了,调用此方法一定是写模式
            readafterDeal();


            SelectionKey key = socketChannel.register(selector, 0);
            key.attach(this);
            key.interestOps(SelectionKey.OP_READ);

        }
        if(readLen==-1){
            socketChannel.close();
        }


    }

    /**
     * 读取数据后进行处理
     * 调用此方法会写模式变成读模式
     */
    public void readafterDeal() throws Exception{
        while(true) {
            //写模式，找到数据是读取模式，否则还是写模式
            int vstartIndex = findStartIndex();
            if (vstartIndex != -1) {
                byte[] bytes = new byte[vstartIndex];
                buffer.get(bytes);
                buffer.get();//分隔符也要处理掉
                //变成写模式，重复查找，永远是写模式
                buffer.compact();
                socketChannel.write(ByteBuffer.wrap(bytes));
            } else {
                //没有找到的情况需要判断是否buffer满了，如果满了抛弃
                if (!buffer.hasRemaining()) {
                    buffer.clear();
                    startIndex = 0;
                    socketChannel.write(ByteBuffer.wrap("no data".getBytes("GBK")));
                }
            }
            //接着下次读取
            if(vstartIndex==-1)
                break;

        }
    }

    /**
     *
     * 获取\n的位置，
     *
     * 找到的话，读模式
     * 没有找到话，写模式
     * @return
     */
    private int findStartIndex(){
        int result=-1;
        buffer.flip();
        int lastPosition =buffer.limit();
        for(int i=startIndex;i<buffer.limit();i++){
            System.out.println("解析的内容："+buffer.get(i));
            if(buffer.get(i)=='\n'){
                result=i;
                break;
            }
        }

        if(result==-1){
            buffer.limit(buffer.capacity());
            startIndex=lastPosition;
            buffer.position(startIndex);
        }else{
            buffer.position(0);
        }
        return result;
    }



}
